﻿using Flurl.Http;
using SCTO.Center;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Vive.Crypto;

namespace testapp.Models
{
    public class SctoModel
    {
        private static SctoModel instance = new SctoModel();
        private string lastKey = string.Empty;
        private string lastFileName = "test";
        public static SctoModel SctoModelInstance => instance;
        public string HostName { get; set; }
        public string? RemoteName { get; set; }

        public event EventHandler FileRecieved;

        private readonly IAsymmetricProvider sm2 = CryptoFactory.CreateAsymmetric(AsymmetricProviderType.SM2);
        private readonly IHashingProvider sm3 = Vive.Crypto.CryptoFactory.CreateHashing(HashingProviderType.SM3);
        private readonly ISymmetricProvider sm4 = Vive.Crypto.CryptoFactory.CreateSymmetric(SymmetricProviderType.SM4);

        private readonly AsymmetricKey sm2Key;
        private UdpClient udpClient;

        public SctoModel()
        {
            HostName = string.Empty;
            sm2Key = sm2.CreateKey();
            udpClient = new UdpClient();
        }

        public bool Init(string myHostName)
        {
            HostName = myHostName;
            var success = IPEndPoint.TryParse(myHostName, out var ip);
            if (!success) return false;
            udpClient = new UdpClient(ip);
            Task.Run(() =>
            {
                while (true)
                {
                    IPEndPoint ep = IPEndPoint.Parse(myHostName);
                    var data = udpClient.Receive(ref ep);
                    var dataStr = Encoding.UTF8.GetString(data);
                    if (dataStr.Contains('|'))
                    {
                        var dataArr = dataStr.Split('|');
                        var remoteEp = IPEndPoint.Parse(dataArr[0]);
                        Console.WriteLine($"接收到来自{dataArr[0]}的文件下载请求:{dataArr[2]}.");
                        var sm4Key = sm2.Decrypt(dataArr[1], sm2Key.PrivateKey);
                        Console.WriteLine($"约定SM4密钥:{sm4Key}");
                        var content = sm4.Encrypt(File.ReadAllText(dataArr[2]), sm4Key);
                        var datagram = Encoding.UTF8.GetBytes(content);
                        Task.Delay(500);
                        var sendClient = new UdpClient();
                        sendClient.SendAsync(datagram, datagram.Length, remoteEp);
                        Console.WriteLine("文件已发送.");
                    }
                    else
                    {
                        Console.Write($"接收到加密文件，使用SM4密钥{lastKey}解密:");
                        File.WriteAllText(lastFileName, sm4.Decrypt(dataStr, lastKey));
                        FileRecieved?.Invoke(this, new());
                    }
                }
            });
            return true;
        }

        public bool ConnectServer(string host)
        {
            RemoteName = host;
            var res = $"{host}/scto/register".PostUrlEncodedAsync(new
            {
                clientId = HostName,
                pubKey = sm2Key.PublickKey
            }).Result;
            return res.StatusCode == 200;
        }

        public IEnumerable<SctoFileInfo> GetFiles()
        {
            var res = $"{RemoteName}/scto/file".GetJsonAsync<List<SctoFileInfo>>().Result;
            return res;
        }

        public IEnumerable<ClientInfo> GetFileInfo(string fileName)
        {
            var res = $"{RemoteName}/scto/file?fileName={fileName}".GetJsonAsync<List<ClientInfo>>().Result;
            return res;
        }

        public bool UploadFile(string file)
        {
            if (!File.Exists(file))
            {
                return false;
            }
            $"{RemoteName}/scto/upload".PostUrlEncodedAsync(new
            {
                clientId = HostName,
                fileName = file,
                fileSignature = sm3.Signature(File.ReadAllText(file), null)
            }).Wait();
            return true;
        }

        public bool DownloadFile(ClientInfo clientInfo, string fileName)
        {
            var parseSuccess = IPEndPoint.TryParse(clientInfo.Client ?? string.Empty, out var fileIp);
            if (!parseSuccess)
            {
                return false;
            }
            var fileSm2Key = clientInfo.PublicKey;
            var random = new Random();
            var key = sm3.Signature(random.NextDouble().ToString(), null);
            var encryptedSm4Key = sm2.Encrypt(key, fileSm2Key);
            lastKey = key;
            lastFileName = fileName;
            var datagram = $"{HostName}|{encryptedSm4Key}|{fileName}";
            var data = Encoding.UTF8.GetBytes(datagram);
            udpClient.Send(data, data.Length, fileIp);
            return true;
        }
    }
}
